/*
 * This file (MathFunction.java) is protected by the GNU GPL licence (v2). 
 * Please read the license.txt attached for further informations. 
 * 
 */
package org.algoristes.alkwarel.script;

import org.algoristes.alkwarel.utils.Rand;

/**
 * @author Xavier Gouchet
 * 
 */
public class MathFunction extends MathNode {

	MathNode[] mParameters;

	/**
	 * @param value
	 */
	public MathFunction(String value) {
		super(value);
		mParameters = new MathNode[] {};
	}

	/**
	 * Sets the parameters of this function
	 * 
	 * @param params
	 */
	public void setParameters(MathNode[] params) {
		mParameters = params;
	}

	/**
	 * @see MathNode#toString()
	 */
	public String toString() {
		if (mParameters == null)
			return mValue;

		String str = mValue + "(";
		for (MathNode n : mParameters) {
			str += n.toString() + ",";
		}
		str += ")";
		return str;
	}

	/**
	 * @see MathNode#eval()
	 */
	public double eval() {
		if (mValue.equals("cos"))
			return evalCosine();

		if (mValue.equals("sin"))
			return evalSine();

		if (mValue.equals("floor"))
			return evalFloor();

		if (mValue.equals("ceil"))
			return evalCeil();

		if (mValue.equals("abs"))
			return evalAbs();

		if (mValue.equals("sqrt"))
			return evalSqrt();

		if (mValue.equals("max"))
			return evalMax();

		if (mValue.equals("min"))
			return evalMin();
		
		if (mValue.equals("rand"))
			return evalRandom();

		return 0;
	}

	/**
	 * Eval the node as a cosine function
	 * 
	 * @return the result of cos(x);
	 */
	public double evalCosine() {

		if (mParameters.length != 1)
			throw new RuntimeException("Cosine needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.cos(a);
	}

	/**
	 * Eval the node as a sine function
	 * 
	 * @return the result of sin(x);
	 */
	public double evalSine() {

		if (mParameters.length != 1)
			throw new RuntimeException("Sine needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.sin(a);
	}

	/**
	 * Eval the node as a floor function
	 * 
	 * @return the result of floor(x);
	 */
	public double evalFloor() {

		if (mParameters.length != 1)
			throw new RuntimeException("Floor needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.floor(a);
	}

	/**
	 * Eval the node as a ceil function
	 * 
	 * @return the result of ceil(x);
	 */
	public double evalCeil() {

		if (mParameters.length != 1)
			throw new RuntimeException("Ceil needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.ceil(a);
	}

	/**
	 * Eval the node as an absolute value function
	 * 
	 * @return the result of abs(x);
	 */
	public double evalAbs() {

		if (mParameters.length != 1)
			throw new RuntimeException("Absolute value needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.abs(a);
	}

	/**
	 * Eval the node as a square root function
	 * 
	 * @return the result of sqrt(x);
	 */
	public double evalSqrt() {

		if (mParameters.length != 1)
			throw new RuntimeException("Square Root needs one parameter");

		double a;
		a = mParameters[0].eval();

		return Math.sqrt(a);
	}

	/**
	 * Eval the node as a maximum function
	 * 
	 * @return the result of max(a, b, ...);
	 */
	public double evalMax() {
		if (mParameters.length <= 1)
			throw new RuntimeException("Maximum needs at least two parameter");

		double max = Double.NEGATIVE_INFINITY;
		double value;
		for (MathNode node : mParameters) {
			value = node.eval();
			if (value > max)
				max = value;
		}

		return max;
	}

	/**
	 * Eval the node as a minimum function
	 * 
	 * @return the result of min(a, b, ...);
	 */
	public double evalMin() {
		if (mParameters.length <= 1)
			throw new RuntimeException("Minimum needs at least two parameter");

		double min = Double.POSITIVE_INFINITY;
		double value;
		for (MathNode node : mParameters) {
			value = node.eval();
			if (value < min)
				min = value;
		}

		return min;
	}

	/**
	 * Eval the node as a random function
	 * 
	 * @return the result of either : rand(), rand(x), rand(min,max)
	 */
	public double evalRandom() {
		if (mParameters.length > 2)
			throw new RuntimeException("Random needs at most two parameter");

		double min = 0.0;
		double max = 1.0;

		if (mParameters.length == 2) {
			min = mParameters[0].eval();
			max = mParameters[1].eval();
		} else if (mParameters.length == 1)
			max = mParameters[0].eval();

		return Rand.getDouble(min, max);
	}
}
